﻿#include "Core.h"

#include "Types.h"
#include "StaticList.h"
#include "Env.h"
#include "Reader.h"
#include "Printer.h"

#include <iostream>
#include <sstream>

namespace Lisp
{
  static StaticList<ValPtr> s_builtInList;

#define BUILTIN_INTOP(op, checkDivByZero)                \
  BUILTIN(#op)                                           \
  {                                                      \
    ARG_COUNT_IS(2);                                     \
    ARG(0, IntegerType, lhs);                            \
    ARG(1, IntegerType, rhs);                            \
    if (checkDivByZero)                                  \
    {                                                    \
      LISP_CHECK(rhs->Value() != 0, "Division by zero"); \
    }                                                    \
    return IntegerVal(lhs->Value() op rhs->Value());     \
  }

  BUILTIN("<")
  {
    ARG_COUNT_IS(2);
    ARG(0, IntegerType, a);
    ARG(1, IntegerType, b);
    return BoolVal(a->Value() < b->Value());
  }

  BUILTIN(">")
  {
    ARG_COUNT_IS(2);
    ARG(0, IntegerType, a);
    ARG(1, IntegerType, b);
    return BoolVal(a->Value() > b->Value());
  }

  BUILTIN("<=")
  {
    ARG_COUNT_IS(2);
    ARG(0, IntegerType, a);
    ARG(1, IntegerType, b);
    return BoolVal(a->Value() <= b->Value());
  }

  BUILTIN(">=")
  {
    ARG_COUNT_IS(2);
    ARG(0, IntegerType, a);
    ARG(1, IntegerType, b);
    return BoolVal(a->Value() >= b->Value());
  }

  BUILTIN("-")
  {
    size_t cnt = ARG_COUNT_BETWEEN(1, 2);
    ARG(0, IntegerType, a);
    if (cnt == 1)
    {
      return IntegerVal(-a->Value());
    }
    ARG(1, IntegerType, b);
    return IntegerVal(a->Value() - b->Value());
  }

  BUILTIN_INTOP(+, false)
  BUILTIN_INTOP(*, false)
  BUILTIN_INTOP(/, true)
  BUILTIN_INTOP(%, true)

  /**
   * @brief 构造一个列表
   *
   * @param args 列表的元素
   */
  BUILTIN("list")
  {
    return args;
  }

  /**
   * @brief 判断一个列表是否为空
   *
   * @param args 列表
   */
  BUILTIN("empty?")
  {
    ARG_COUNT_IS(1);
    ARG(0, SequenceType, seq);
    return BoolVal(seq->IsEmpty());
  }

  /**
   * @brief 取列表的元素个数
   *
   * @param args 列表
   */
  BUILTIN("count")
  {
    ARG_COUNT_IS(1);
    if (args->At(0) == NilVal())
    {
      return args->At(0);
    }
    ARG(0, SequenceType, seq);
    return IntegerVal(seq->Size());
  }

  BUILTIN("=")
  {
    ARG_COUNT_IS(2);
    return args->At(0)->Equals(args->At(1).get()) ? TrueVal() : FalseVal();
  }

  // 字符串
  static std::string Print(ListPtr args, const char *delim = " ", bool readably = true)
  {
    std::stringstream ss;
    for (int i = 0; i < args->Size(); i++)
    {
      ss << PrintStr(args->At(i), readably);
      if (i < args->Size() - 1)
      {
        ss << delim;
      }
    }
    return ss.str();
  }

  BUILTIN("nth")
  {
    ARG_COUNT_IS(2);
    ARG(0, SequenceType, seq);
    ARG(1, IntegerType, index);

    int i = index->Value();
    LISP_CHECK(i >= 0 && i < seq->Size(), "Index out of range");

    return seq->At(i);
  }

  BUILTIN("first")
  {
    ARG_COUNT_IS(1);
    ARG(0, SequenceType, seq);
    return seq->First();
  }

  BUILTIN("rest")
  {
    ARG_COUNT_IS(1);
    ARG(0, SequenceType, seq);
    return seq->Rest();
  }

  /**
   * @brief 输入列表，拼接成返回字符串，每个元素之间用空格分隔，避开转义字符
   *
   * @param args 列表
   */
  BUILTIN("pr-str")
  {
    return StringVal(Print(args));
  }

  /**
   * @brief 输入列表，拼接成返回字符串
   *
   * @param args 列表
   */
  BUILTIN("str")
  {
    return StringVal(Print(args, "", false));
  }

  /**
   * @brief 打印一个字符串，每个元素之间用空格分隔，避开转义字符，返回nil
   *
   * @param args 列表
   */
  BUILTIN("prn")
  {
    std::cout << Print(args) << std::endl;
    return NilVal();
  }

  /**
   * @brief 打印一个字符串，每个元素之间用空格分隔
   *
   * @param args 要打印的字符串
   */
  BUILTIN("println")
  {
    std::cout << Print(args, " ", false) << std::endl;
    return NilVal();
  }

  /**
   * @brief 读取一个字符串，返回一个list
   *
   * @param args 要读取的字符串
   */
  BUILTIN("read-string")
  {
    ARG_COUNT_IS(1);
    ARG(0, StringType, str);
    return ReadStr(str->Value());
  }

  /**
   * @brief 读取一个文件，返回一个字符串
   *
   * @param args 要读取的文件名
   */
  BUILTIN("slurp")
  {
    ARG_COUNT_IS(1);
    ARG(0, StringType, fileName);
    std::string content = FileUtils::ReadFile(fileName->Value());
    return StringVal(content);
  }

  /**
   * @brief 读取一个文件，返回一个list，每个元素是一行
   *
   * @param args 要读取的文件名
   */
  BUILTIN("eval")
  {
    ARG_COUNT_IS(1);
    return EVAL(args->At(0), nullptr);
  }

  BUILTIN("apply")
  {
    ARG_COUNT_AT_LEAST(2);
    ARG(0, ValType, op);

    // Copy the first N-1 arguments in.
    ValVec argv(args->begin() + 1, args->end() - 1);

    // Then append the argument as a list.
    const SequenceType *lastArg = VALUE_CAST(SequenceType, args->At(args->Size() - 1));
    for (int i = 0; i < lastArg->Size(); i++)
    {
      argv.push_back(lastArg->At(i));
    }

    return APPLY(op, new ListType(argv.begin(), argv.end()));
  }

  BUILTIN("map")
  {
    ARG_COUNT_IS(2);
    ARG(0, ValType, op);
    ARG(1, SequenceType, source);

    const int length = source->Size();
    ValVec *items = new ValVec(length);
    auto it = source->begin();
    for (int i = 0; i < length; i++)
    {
      items->at(i) = APPLY(op, new ListType(it + i, it + i + 1));
    }
    return new ListType(items);
  }

  BUILTIN("foreach")
  {
    ARG_COUNT_IS(2);
    ARG(0, SequenceType, source);
    ARG(1, ValType, op);

    const int length = source->Size();
    ValVec *items = new ValVec(length);
    auto it = source->begin();
    for (int i = 0; i < length; i++)
    {
      items->at(i) = APPLY(op, new ListType(it + i, it + i + 1));
    }
    return NilVal();
  }

  BUILTIN("throw")
  {
    ARG_COUNT_IS(1);
    throw args->At(0);
  }

  /**
   * @brief 构造一个atom
   *
   * @param args 要构造的atom的值
   */
  BUILTIN("atom")
  {
    ARG_COUNT_IS(1);
    return AtomVal(args->At(0));
  }

  /**
   * @brief 解引用一个atom
   *
   * @param atom 要解引用的atom
   */
  BUILTIN("deref")
  {
    ARG_COUNT_IS(1);
    ARG(0, AtomType, atom);
    return atom->Deref();
  }

  /**
   * @brief 重置一个atom的值
   *
   * @param atom 要重置的atom
   * @param val 要重置的值
   */
  BUILTIN("reset!")
  {
    ARG_COUNT_IS(2);
    ARG(0, AtomType, atom);
    ARG(1, ValType, val);
    return atom->Reset(val);
  }

  /**
   * @brief 交换一个atom的值
   *
   * @param atom 要交换的atom
   * @param op 要交换的函数
   * @param args 要交换的函数的参数
   */
  BUILTIN("swap!")
  {
    ARG_COUNT_AT_LEAST(2);
    ARG(0, AtomType, atom);
    ARG(1, ValType, op);
    ValVec argv(args->Size() - 1);
    argv[0] = atom->Deref();
    std::copy(args->begin() + 2, args->end(), argv.begin() + 1);
    ValPtr value = APPLY(op, new ListType(argv.begin(), argv.end()));
    return atom->Reset(value);
  }

  BUILTIN("hash-map")
  {
    return HashVal(args->begin(), args->end(), true);
  }

  BUILTIN("get")
  {
    ARG_COUNT_IS(2);
    if (args->At(0) == NilVal())
    {
      return args->At(0);
    }
    ARG(0, HashType, hash);
    return hash->Get(args->At(1));
  }

  BUILTIN("keys")
  {
    ARG_COUNT_IS(1);
    ARG(0, HashType, hash);
    return hash->Keys();
  }

  BUILTIN("vals")
  {
    ARG_COUNT_IS(1);
    ARG(0, HashType, hash);
    return hash->Values();
  }

  BUILTIN("assoc")
  {
    ARG_COUNT_AT_LEAST(1);
    ARG(0, HashType, hash);
    return hash->Assoc(args->begin() + 1, args->end());
  }

  BUILTIN("dissoc")
  {
    ARG_COUNT_AT_LEAST(1);
    ARG(0, HashType, hash);
    return hash->Assoc(args->begin() + 1, args->end());
  }

  BUILTIN("contains?")
  {
    ARG_COUNT_IS(2);
    if (args->At(0) == NilVal())
    {
      return args->At(0);
    }
    ARG(0, HashType, hash);
    ARG(1, ValType, key);
    return BoolVal(hash->Contains(key));
  }

  BUILTIN("conj")
  {
    ARG_COUNT_AT_LEAST(1);
    ARG(0, SequenceType, seq);

    return seq->Conj(args->begin() + 1, args->end());
  }

  BUILTIN("cons")
  {
    ARG_COUNT_IS(2);
    ARG(0, ValType, first);
    ARG(1, SequenceType, rest);
    ValVec *items = new ValVec(rest->Size() + 1);
    items->at(0) = first;
    std::copy(rest->begin(), rest->end(), items->begin() + 1);
    return ListVal(items);
  }

  BUILTIN("concat")
  {
    int count = 0;
    for (int i = 0; i < args->Size(); i++)
    {
      const SequenceType *seq = VALUE_CAST(SequenceType, args->At(i));
      count += seq->Size();
    }

    ValVec *res = new ValVec(count);
    int offset = 0;
    for (int i = 0; i < args->Size(); i++)
    {
      const SequenceType *seq = STATIC_CAST(SequenceType, args->At(i));
      std::copy(seq->begin(), seq->end(), res->begin() + offset);
      offset += seq->Size();
    }
    return ListVal(res);
  }

  BUILTIN("vec")
  {
    ARG_COUNT_IS(1);
    ARG(0, SequenceType, seq)
    return VectorVal(seq->begin(), seq->end());
  }

  BUILTIN("vector")
  {
    return VectorVal(args->begin(), args->end());
  }

  BUILTIN("symbol")
  {
    ARG_COUNT_IS(1);
    ARG(0, StringType, str);
    return SymbolVal(str->Value());
  }

  BUILTIN("keyword")
  {
    ARG_COUNT_IS(1);
    if (KeywordType *s = DYNAMIC_CAST(KeywordType, args->At(0)))
      return s;
    if (const StringType *s = DYNAMIC_CAST(StringType, args->At(0)))
      return KeywordVal(s->Value());
    LISP_CHECK(false, "Keyword expects a keyword or string");
  }

  BUILTIN("seq")
  {
    ARG_COUNT_IS(1);
    ValPtr arg = args->At(0);
    if (arg == NilVal())
    {
      return NilVal();
    }
    if (const SequenceType *seq = DYNAMIC_CAST(SequenceType, arg))
    {
      return seq->IsEmpty() ? NilVal()
                            : ListVal(seq->begin(), seq->end());
    }
    if (const StringType *strVal = DYNAMIC_CAST(StringType, arg))
    {
      const std::string str = strVal->Value();
      int length = str.length();
      if (length == 0)
        return NilVal();

      ValVec *items = new ValVec(length);
      for (int i = 0; i < length; i++)
      {
        (*items)[i] = StringVal(str.substr(i, 1));
      }
      return ListVal(items);
    }
    LISP_CHECK(false, "%s is not a string or sequence", arg->ToString(true).c_str());
  }

  BUILTIN("meta")
  {
    ARG_COUNT_IS(1);
    return args->At(0)->Meta();
  }

  BUILTIN("with-meta")
  {
    ARG_COUNT_IS(2);
    ARG(0, ValType, val);
    ARG(1, ValType, meta);
    return val->WithMeta(meta);
  }

  BUILTIN("readline")
  {
    ARG_COUNT_IS(1);
    ARG(0, StringType, prompt);
    return Readline(prompt->Value());
  }

  BUILTIN("time-ms")
  {
    ARG_COUNT_IS(0);
    return IntegerVal(TimeUtils::Now());
  }

  BUILTIN("range")
  {
    ARG_COUNT_BETWEEN(2, 3);
    ARG(0, IntegerType, start);
    ARG(1, IntegerType, end);
    int s = start->Value();
    int e = end->Value();
    LISP_CHECK(s <= e, "Start must be less than or equal to end");

    int st = 1;
    if (args->Size() == 3)
    {
      ARG(2, IntegerType, step);
      st = step->Value();
    }

    int length = (e - s) / st + 1;
    ValVec *items = new ValVec(length);

    for (int i = 0, j = 0; i < length; i++, j += st)
    {
      (*items)[i] = IntegerVal(s + j);
    }
    return VectorVal(items);
  }

  // 类型判断

  /**
   * @brief 判断一个值是否是nil
   *
   * @param args 要判断的值
   */
  BUILTIN("nil?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(args->At(0) == NilVal());
  }

  /**
   * @brief 判断一个值是否是true
   *
   * @param args 要判断的值
   */
  BUILTIN("true?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(args->At(0) == TrueVal());
  }
  /**
   * @brief 判断一个值是否是false
   *
   * @param args 要判断的值
   */
  BUILTIN("false?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(!args->At(0) == FalseVal());
  }

  /**
   * @brief 判断一个值是否是符号
   *
   * @param args 要判断的值
   */
  BUILTIN("symbol?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(SymbolType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是数字
   *
   * @param args 要判断的值
   */
  BUILTIN("number?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(IntegerType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是函数
   *
   * @param args 要判断的值
   */
  BUILTIN("fn?")
  {
    ARG_COUNT_IS(1);
    ValPtr arg = args->At(0);

    // Lambdas are functions, unless they're macros.
    if (const LambdaType *lambda = DYNAMIC_CAST(LambdaType, arg))
    {
      return BoolVal(!lambda->IsMacro());
    }
    // Builtins are functions.
    return BoolVal(DYNAMIC_CAST(BuiltInFuncType, arg));
  }

  /**
   * @brief 判断一个值是否是宏
   *
   * @param args 要判断的值
   */
  BUILTIN("macro?")
  {
    ARG_COUNT_IS(1);
    LambdaType *lambda = DYNAMIC_CAST(LambdaType, args->At(0));
    return BoolVal(lambda != nullptr && lambda->IsMacro());
  }

  /**
   * @brief 判断一个值是否是列表或向量
   *
   * @param args 要判断的值
   */
  BUILTIN("sequential?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(SequenceType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是atom
   *
   * @param args 要判断的值
   */
  BUILTIN("atom?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(AtomType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是列表
   *
   * @param args 要判断的值
   */
  BUILTIN("list?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(ListType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是向量
   *
   * @param args 要判断的值
   */
  BUILTIN("vector?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(VectorType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是哈希表
   *
   * @param args 要判断的值
   */
  BUILTIN("map?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(HashType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是关键字
   *
   * @param args 要判断的值
   */
  BUILTIN("keyword?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(KeywordType, args->At(0)));
  }

  /**
   * @brief 判断一个值是否是字符串
   *
   * @param args 要判断的值
   */
  BUILTIN("string?")
  {
    ARG_COUNT_IS(1);
    return BoolVal(DYNAMIC_CAST(StringType, args->At(0)));
  }

  // 初始化
  void InitNativeBuiltIns(EnvPtr env)
  {
    for (auto &node : s_builtInList)
    {
      env->Set(node.Symbol(), node.Item());
    }
  }
}